#include "uart_v2.hpp"

#include <glog/logging.h>
uart_v2::uart_v2(std::string port_name,
                 int baud_rate,
                 int data_bits,
                 int stop_bits) : _port_name(port_name),
                                  _baud_rate(baud_rate),
                                  _data_bits(data_bits),
                                  _stop_bits(stop_bits)
{
    open_flag = YJ_ERROR;
    _send_additonal_byte_time = 0.7;
}

uart_v2::uart_v2(std::string port_name,
                 int baud_rate,
                 int data_bits,
                 int stop_bits,
                 float send_additonal_byte_time) : _port_name(port_name),
                                                   _baud_rate(baud_rate),
                                                   _data_bits(data_bits),
                                                   _stop_bits(stop_bits),
                                                   _send_additonal_byte_time(send_additonal_byte_time)
{
    open_flag = YJ_ERROR;
    if (_send_additonal_byte_time < -0.5 || _send_additonal_byte_time > 2.0)
    {
        LOG(WARNING) << "[uart_v2][uart_v2][port_name: " << _port_name << "]:" << "send_additonal_byte_time: " << _send_additonal_byte_time << " may cause send data error or recv data error";
    }
}

std::mutex& uart_v2::uart_get_mutex()
{
    return _uart_mutex;
}
yj_return uart_v2::uart_open()
{
    try
    {
        // Open the Serial Ports at the desired hardware devices.
        // if (open_flag != YJ_OK)
        if(!_port.IsOpen())
            _port.Open(_port_name);
    }
    catch (const sl::OpenFailed &)
    {
        LOG(ERROR) << "[uart_v2][uart_open][port_name: " << _port_name << "]:" << "The serial ports did not open correctly.";
        return YJ_ERROR;
    }

    LOG(INFO) << "[uart_v2][uart_open][port_name: " << _port_name << "]:" << "uart opened";

    switch (_baud_rate)
    {
    case 9600:
        _port.SetBaudRate(sl::BaudRate::BAUD_9600);
        break;
    case 19200:
        _port.SetBaudRate(sl::BaudRate::BAUD_19200);
        break;
    case 38400:
        _port.SetBaudRate(sl::BaudRate::BAUD_38400);
        break;
    case 57600:
        _port.SetBaudRate(sl::BaudRate::BAUD_57600);
        break;
    case 115200:
        _port.SetBaudRate(sl::BaudRate::BAUD_115200);
        break;
    case 230400:
        _port.SetBaudRate(sl::BaudRate::BAUD_230400);
        break;
    case 460800:
        _port.SetBaudRate(sl::BaudRate::BAUD_460800);
        break;
    case 500000:
        _port.SetBaudRate(sl::BaudRate::BAUD_500000);
        break;
    case 576000:
        _port.SetBaudRate(sl::BaudRate::BAUD_576000);
        break;
    case 921600:
        _port.SetBaudRate(sl::BaudRate::BAUD_921600);
        break;
    default:
        LOG(ERROR) << "[uart_v2][uart_open][port_name: " << _port_name << "]:" << "uart baud rate error";
        _port.Close();
        return YJ_ERROR;
    }

    switch (_data_bits)
    {
    case 5:
        _port.SetCharacterSize(sl::CharacterSize::CHAR_SIZE_5);
        break;
    case 6:
        _port.SetCharacterSize(sl::CharacterSize::CHAR_SIZE_6);
        break;
    case 7:
        _port.SetCharacterSize(sl::CharacterSize::CHAR_SIZE_7);
        break;
    case 8:
        _port.SetCharacterSize(sl::CharacterSize::CHAR_SIZE_8);
        break;
    default:
        LOG(ERROR) << "[uart_v2][uart_open][port_name: " << _port_name << "]:" << "uart data bits error";
        _port.Close();
        return YJ_ERROR;
    }

    // Set the hardware flow control.
    _port.SetFlowControl(sl::FlowControl::FLOW_CONTROL_NONE);

    // Set the parity.
    _port.SetParity(sl::Parity::PARITY_NONE);

    switch (_stop_bits)
    {
    case 1:
        _port.SetStopBits(sl::StopBits::STOP_BITS_1);
        break;
    case 2:
        _port.SetStopBits(sl::StopBits::STOP_BITS_2);
        break;
    default:
        LOG(ERROR) << "[uart_v2][uart_open][port_name: " << _port_name << "]:" << "uart stop bits error";
        _port.Close();
        return YJ_ERROR;
    }
    _byte_timeout_ms = (1 * 10) * 1000 * 3 / _baud_rate; // 单个字节超时时间为三个字节的完整传送时间
    if (_byte_timeout_ms < 10)
    {
        _byte_timeout_ms = 10;
    }
    open_flag = YJ_OK;

    uart_print_config();

end:
    return YJ_OK;
}

uart_v2::~uart_v2()
{
    uart_close();
}

yj_return uart_v2::uart_close()
{
    _port.Close();
    open_flag = YJ_ERROR;
    return YJ_OK;
}

int uart_v2::uart_send(const uint8_t *data, int len)
{
    sl::DataBuffer buff;
    uint32_t delay_time = 0;
    if (open_flag != YJ_OK || !data)
    {
        LOG(ERROR) << "[uart_v2][uart_send][port_name: " << _port_name << "]:" << "uart not open";
        return 0;
    }
    buff.resize(len);
    memcpy(buff.data(), data, len);

    // delay_time = (len) * 10 * 1000000 * 1 / _baud_rate + 0.7 * 10 * 1000000 * 1 / _baud_rate ; // 1倍波特率时间 + 0.7字节波特率时间延时
    delay_time = (len) * 10 * 1000000 * 1 / _baud_rate + _send_additonal_byte_time * 10 * 1000000 * 1 / _baud_rate;
    // LOG(INFO) << "[uart_v2][uart_send][port_name: " << _port_name << "]:" << "uart send len: " << len << " delay_time: " << delay_time;

    try
    {
        _port.Write(buff);

        // 延时
        std::this_thread::sleep_for(std::chrono::microseconds(delay_time));
        // _port.DrainWriteBuffer();//该函数为保证数据完全发送会造成较长时间延迟，故不使用
    }
    catch (const std::runtime_error &)
    {
        return 0;
    }

    return len;
}

int uart_v2::uart_recv(uint8_t *data, int len)
{
    sl::DataBuffer buff;
    uint32_t time_out = 0;
    if (open_flag != YJ_OK || !data)
    {
        LOG(ERROR) << "[uart_v2][uart_recv][port_name: " << _port_name << "]:" << "uart not open";
        return 0;
    }
    buff.resize(len);
    // _port.SetVTime(10); // 1s
    try
    {
        _port.Read(buff, len, 1000); // 1s
                                     // LOG(INFO) << "[uart_v2][uart_recv][port_name: " << _port_name << "]:" << "vtime :" << _port.GetVTime();
                                     // if (_port.GetNumberOfBytesAvailable() > 0)
                                     // {
                                     //     _port.Read(buff, 1000);
                                     // }
    }
    catch (const sl::ReadTimeout &)
    {
        LOG(ERROR) << "[uart_v2][uart_recv][port_name: " << _port_name << "]:" << "read with length byte timed out.";
        return 0;
    }
    memcpy(data, buff.data(), len);
    return len;
}

int uart_v2::uart_recv_one_by_one(uint8_t *data, int max_len, int frame_timeout_ms)
{
    if (open_flag != YJ_OK || !data)
    {
        LOG(ERROR) << "[uart_v2][uart_recv_one_by_one][port_name: " << _port_name << "]:" << "uart not open";
        return 0;
    }

    int offset = 0;
    try
    {
        _port.ReadByte(*data, frame_timeout_ms);
    }
    catch (const sl::ReadTimeout &)
    {
        LOG(ERROR) << "[uart_v2][uart_recv][port_name: " << _port_name << "]:" << "read first byte timed out.";
        return 0;
    }
    offset++;
    while (1)
    {

        try
        {
            if (_port.GetNumberOfBytesAvailable() > 0)
            {
                _port.ReadByte(*(data + offset), _byte_timeout_ms);
                offset++;
                if (offset >= max_len)
                {
                    if (_port.GetNumberOfBytesAvailable() > 0)
                        LOG(WARNING) << "[uart_v2][uart_recv_one_by_one][port_name: " << _port_name << "]:" << "mem overflow but still can recv data";
                    break;
                }
            }
            else
            {
                std::this_thread::sleep_for(std::chrono::milliseconds(_byte_timeout_ms));
                if (_port.GetNumberOfBytesAvailable() <= 0)
                    break;
            }

            // _port.ReadByte(*(data + offset), 0);
        }
        catch (const sl::ReadTimeout &)
        {
            LOG(ERROR) << "[uart_v2][uart_recv][port_name: " << _port_name << "]:" << "read bytes timed out.";

            return offset;
        }
    }
    return offset;
}

std::string uart_v2::uart_get_port_name()
{
    return _port_name;
}

uart_v2 &uart_v2::uart_change_baud_rate(int baud_rate)
{
    if (baud_rate == _baud_rate)
        return *this;

    switch (baud_rate)
    {
    case 9600:
        _port.SetBaudRate(sl::BaudRate::BAUD_9600);
        break;
    case 19200:
        _port.SetBaudRate(sl::BaudRate::BAUD_19200);
        break;
    case 38400:
        _port.SetBaudRate(sl::BaudRate::BAUD_38400);
        break;
    case 57600:
        _port.SetBaudRate(sl::BaudRate::BAUD_57600);
        break;
    case 115200:
        _port.SetBaudRate(sl::BaudRate::BAUD_115200);
        break;
    case 230400:
        _port.SetBaudRate(sl::BaudRate::BAUD_230400);
        break;
    case 460800:
        _port.SetBaudRate(sl::BaudRate::BAUD_460800);
        break;
    case 500000:
        _port.SetBaudRate(sl::BaudRate::BAUD_500000);
        break;
    case 576000:
        _port.SetBaudRate(sl::BaudRate::BAUD_576000);
        break;
    case 921600:
        _port.SetBaudRate(sl::BaudRate::BAUD_921600);
        break;
    default:
        LOG(ERROR) << "[uart_v2][uart_open][port_name: " << _port_name << "]:" << "uart baud rate error";
        break;
    }
    _baud_rate = baud_rate;
    return *this;
}

uart_v2 &uart_v2::uart_change_send_additonal_btye_time(float send_additonal_byte_time)
{
    _send_additonal_byte_time = send_additonal_byte_time;
    return *this;
}
void uart_v2::uart_flush_all()
{
    _port.FlushIOBuffers();
}

void uart_v2::uart_print_config()
{
    LOG(INFO) << "[uart_v2][uart_config][port_name: " << _port_name << "]:\n"
              << "baudrate: " << _baud_rate << "\n"
              << "data_bits: " << _data_bits << "\n"
              << "stop_bits: " << _stop_bits << "\n"
              << "open_flag: " << open_flag << "\n"
              << "byte_timeout_ms: " << _byte_timeout_ms << "\n";
}
